package com.sunday.common.http.apache.async;

import com.alibaba.csp.sentinel.Entry;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.async.AsyncExecCallback;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.nio.AsyncDataConsumer;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 实现了Apache HttpClient异步HTTP客户端库中的AsyncExecCallback接口，主要用于处理异步HTTP请求的各个阶段的回调结果。
 * DefaultAsyncExecCallback类实现了AsyncExecCallback接口，这个接口是Apache HttpClient库中用于处理异步HTTP请求的回调接口。这个类通常在异步HTTP请求的各个阶段中使用，例如处理响应、处理信息响应、完成请求处理和处理失败的请求。
 * 在这个类中，有一个AtomicReference 类型的成员变量 entityConsumerRef，它是线程安全的，用于存储Entry类型的对象，也就是刚刚在SentinelApacheHttpAsyncClientBuilder类中创建的资源追踪器的入口。另一个成员变量asyncExecCallback也是AsyncExecCallback接口的实例，它被包装进当前类，并在每个方法中调用。
 * 当处理响应、处理信息响应和处理失败请求的方法被调用时，exit方法都会被触发，这个方法将获取entityConsumerRef中的Entry，并将其设置为null，然后调用该entry的exit方法以释放资源。这是一个通用的模式，确保了即使在发生错误时，资源也能被正确地释放。
 * 综上所述，这个类就是对Apache HttpClient的异步回调接口AsyncExecCallback的一个封装类，将请求中与Sentinel相关的操作集中处理，提高了代码的可读性和维护性。同时，它也与Sentinel实现了紧密的整合，添加了请求的限流和熔断功能。
 *
 * @author zsy
 * @since 2024/1/18
 */
@Slf4j
public class DefaultAsyncExecCallback implements AsyncExecCallback {

    private final AtomicReference<Entry> entityConsumerRef = new AtomicReference<>();

    private final AsyncExecCallback asyncExecCallback;

    public DefaultAsyncExecCallback(Entry entry, AsyncExecCallback asyncExecCallback) {
        this.entityConsumerRef.set(entry);
        this.asyncExecCallback = asyncExecCallback;
    }

    @Override
    public AsyncDataConsumer handleResponse(HttpResponse response, EntityDetails entityDetails) throws HttpException, IOException {
        exit();
        return asyncExecCallback.handleResponse(response, entityDetails);
    }

    @Override
    public void handleInformationResponse(HttpResponse response) throws HttpException, IOException {
        exit();
        asyncExecCallback.handleInformationResponse(response);
    }

    @Override
    public void completed() {
        asyncExecCallback.completed();
    }

    @Override
    public void failed(Exception cause) {
        exit();
        asyncExecCallback.failed(cause);
    }

    public void exit() {
        Entry entry = entityConsumerRef.getAndSet(null);
        if (entry != null) {
            entry.exit();
        }
    }

}
